/*
 * 游戏服务器管理中心
 */
package com.icee.myth.manager;

import com.icee.myth.common.channelContext.ChannelContext;
import com.icee.myth.common.message.serverMessage.Message;
import com.icee.myth.common.message.serverMessage.builder.MessageBuilder;
import com.icee.myth.common.messageQueue.ServerMessageQueue;
import com.icee.myth.common.processOutputThread.ProcessOutputThread;
import com.icee.myth.common.protobufMessage.ProtobufMessage;
import com.icee.myth.manager.config.ServerConfig;
import com.icee.myth.manager.db.DbHandler;
import com.icee.myth.manager.message.serverMessage.*;
import com.icee.myth.manager.pipelineFactory.BootstrapToManagerPipelineFactory;
import com.icee.myth.manager.pipelineFactory.ConsoleToManagerPipelineFactory;
import com.icee.myth.manager.pipelineFactory.DeamonToManagerPipelineFactory;
import com.icee.myth.manager.pipelineFactory.HttpServerToWebPipelineFactory;
import com.icee.myth.manager.server.Server;
import com.icee.myth.manager.server.state.ShutdownServerState;
import com.icee.myth.manager.server.state.UnknownServerState;
import com.icee.myth.manager.web.WebServer;
import com.icee.myth.protobuf.builder.ConsoleToManagerBuilder;
import com.icee.myth.utils.*;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.security.KeyStore;
import java.security.Security;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;

import static com.icee.myth.utils.Consts.HEARTBEAT_PERIOD;

/**
 * Manager接受并处理管理员从Console发来的命令。
 * Manager通过向Deamon发送指令管控多台独立机器上Myth游戏服务器运行。
 * 各Deamon启动后都会向
 *
 * @author liuxianke
 */
public class Manager implements Runnable {

    /**
     * Singleton，没有多线程同步的需要，只是方便全局访问
     */
    final public static Manager INSTANCE = new Manager();

    /**
     * 全局共享的消息队列
     */
    private final LinkedTransferQueue<Message> messageQueue = ServerMessageQueue.queue();

    private ConsoleToManagerServer consoleToManagerServer;
    private DeamonToManagerServer deamonToManagerServer;
    private BootstrapToManagerServer bootstrapToManagerServer;

    /**
     * manager服务器所管理的所有游戏服务器，KEY：游戏服务器的ID，V：游戏服务器的抽象
     */
    private ConcurrentHashMap<Integer, Server> servers;
    private HashSet<String> trustedConsoleHosts = new HashSet<String>();

    /**
     * 与bootstrap连接的端口
     */
    private int bootstrap2Manager_port;
    /**
     * 与console连接的端口
     */
    private int console2Manager_port;
    /**
     * 与deamon连接的端口
     */
    private int deamon2Manager_port;

    /**
     * 启动web服务的ip
     */
    public String webHost = null;
    /**
     * 启动web服务的端口
     */
    public int webPort;
    public WebServer webServer;
    public int maxSessionNum = 100000;
    public int sessionPeriod = 3600000;
//    public MimetypesFileTypeMap mimeTypesMap ;

    /**
     * 心跳计时器
     */
    private Timer heartbeatTimer;

    /**
     * 数据库地址
     */
    public String dbHost;
    /**
     * 数据库名
     */
    public String dbName;
//    private int keepAliveInterval = Consts.DB_KEEPALIVE_INTERVAL;

    /**
     * 操作系统名
     */
    public String osName;
    /**
     * 启动模式（0.本地资源加载/1.远程资源加载）
     */
    public int mode;
    /**
     * 服务器JAR包名称
     */
    public String serverJarFileName;
    /**
     * 程序源服务器地址
     */
    private String sourceHost;
    /**
     * 程序源服务器端口
     */
    private int sourcePort;
    /**
     * 服务器程序数据
     */
    public byte[] sourceData;

    /**
     * 启动Manager Server 的入口方法
     *
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        if (args[0].compareTo("-version") == 0) {
            System.out.println("Manager version : " + Version.getBuildVersion());
            return;
        }
        // initialize logger
        MLogger.init("manager", LogConsts.LOGFLUSH_INTERVAL);
        // 从配置文件读取配置信息
        if (!Manager.INSTANCE.parseArgs(args)) {
            System.err.println("错误,从配置文件读取配置信息失败");
            return;
        }
        //验证请求主机，初始化需要的启动的服务器信息
        if (!Manager.INSTANCE.init()) {
            System.err.println("错误,验证请求主机，初始化需要的启动的服务器信息失败");
            return;
        }
        Manager.INSTANCE.startOnlyOnce();
    }

    //region 解析启动main的参数

    /**
     manager程序启动参数说明:
     完整参数：
     java -ea -Dfile.encoding=UTF-8 -classpath dist\ProjectKManagerServer.jar com.icee.myth.manager.Manager -maxSessionNum 100000 -sessionPeriod 3600000 -deamonPort 4768 -bootstrapPort 4769 -consolePort 4678 -webHost 192.168.100.209 -webPort 8083 -dbHost 192.168.100.209 -dbName sanguo_manager -mode 0 -serverJar ProjectKServer.jar -osName windows

     1. 不需要变化的参数
     java -ea -Dfile.encoding=UTF-8 -classpath dist\ProjectKManagerServer.jar com.icee.myth.manager.Manager -serverJar ProjectKServer.jar

     2. 其他需要变化的参数
     -maxSessionNum 【最多可以有几个玩家同时在线数】
     -sessionPeriod 【session过期时间】
     -deamonPort 【deamon端口】
     -bootstrapPort 【消息转发端口】
     -consolePort 【console端口】
     -webHost 【Manager服务器IP】
     -webPort 【Manager监听的端口】
     -dbHost 【数据库ip】
     -dbName 【manager数据库名称】
     -mode 【启动类型 0或者1, 开发时配置为0，正式运营配置成1】
     -osName 【系统类型 windows | linux】
     * */

    /**
     * 遍历系统控制台传入参数，找到指定需要的值
     *
     * @param args
     * @param param
     * @return
     */
    private String parseArg(String[] args, String param) {
        int argsNum = args.length;
        String retVal = null;
        for (int i = 0; i < argsNum; i++) {
            if (args[i].compareTo(param) == 0) {
                if (i + 1 < argsNum) {
                    retVal = args[i + 1];
                }
                break;
            }
        }

        return retVal;
    }

    private boolean parseArgs(String[] args) {
        // parse bootstrap port
        String bootstrapPortStr = parseArg(args, "-bootstrapPort");
        if (bootstrapPortStr != null) {
            Integer bootstrapPort = Integer.valueOf(bootstrapPortStr);
            if (bootstrapPort != null) {
                bootstrap2Manager_port = bootstrapPort;
            } else {
                System.err.println("Value of \"-bootstrapPort\" param must be integer.");
                return false;
            }
        } else {
            System.err.println("Lack \"-bootstrapPort XXX\" param");
            return false;
        }

        // parse deamon port
        String deamonPortStr = parseArg(args, "-deamonPort");
        if (deamonPortStr != null) {
            Integer deamonPort = Integer.valueOf(deamonPortStr);
            if (deamonPort != null) {
                deamon2Manager_port = deamonPort;
            } else {
                System.err.println("Value of \"-deamonPort\" param must be integer.");
                return false;
            }
        } else {
            System.err.println("Lack \"-deamonPort XXX\" param");
            return false;
        }

        // parse console port
        String consolePortStr = parseArg(args, "-consolePort");
        if (consolePortStr != null) {
            Integer consolePort = Integer.valueOf(consolePortStr);
            if (consolePort != null) {
                console2Manager_port = consolePort;
            } else {
                System.err.println("Value of \"-consolePort\" param must be integer.");
                return false;
            }
        } else {
            System.err.println("Lack \"-consolePort XXX\" param");
            return false;
        }

        // parse private host
        String webHostString = parseArg(args, "-webHost");
        if (webHostString != null) {
            if (CommonUtil.validateIPAddress(webHostString)) {
                if (CommonUtil.validateLocalAddress(webHostString)) {
                    webHost = webHostString;
                } else {
                    System.err.println("Value of \"-webHost\" param must be a ip of the local machine which run this manager.");
                    return false;
                }
            } else {
                System.err.println("Value of \"-webHost\" param must be a ip address.");
                return false;
            }
        } else {
            System.err.println("Lack \"-webHost XXX\" param");
            return false;
        }

        // parse web port
        String webPortStr = parseArg(args, "-webPort");
        if (webPortStr != null) {
            Integer webPortI = Integer.valueOf(webPortStr);
            if (webPortI != null) {
                webPort = webPortI;
            } else {
                System.err.println("Value of \"-webPort\" param must be integer.");
                return false;
            }
        } else {
            System.err.println("Lack \"-webPort XXX\" param");
            return false;
        }

        // parse private host
        String dbHostString = parseArg(args, "-dbHost");
        if (dbHostString != null) {
            if (dbHostString.compareTo("localhost") == 0 || CommonUtil.validateIPAddress(dbHostString)) {
                dbHost = dbHostString;
            } else {
                System.err.println("Value of \"-dbHost\" param must be a ip address.");
                return false;
            }
        } else {
            System.err.println("Lack \"-dbHost XXX\" param");
            return false;
        }

        // parse private host
        dbName = parseArg(args, "-dbName");
        if (dbName == null) {
            System.err.println("Lack \"-dbName XXX\" param");
            return false;
        }

        String modeStr = parseArg(args, "-mode");
        if (modeStr != null) {
            Integer modeI = Integer.valueOf(modeStr);
            if (modeI != null) {
                mode = modeI;
            } else {
                System.err.println("Value of \"-mode\" param must be 0 or 1.");
                return false;
            }
        } else {
            System.err.println("Lack \"-mode XXX\" param");
            return false;
        }

        if (mode == Consts.START_MODE_LOCAL) {
            // parse jar file name
            serverJarFileName = parseArg(args, "-serverJar");
            if (serverJarFileName == null) {
                System.err.println("Lack \"-serverJar XXX\" param");
                return false;
            }
        } else {
            // parse private host
            sourceHost = parseArg(args, "-sourceHost");
            if (sourceHost == null) {
                System.err.println("Lack \"-sourceHost XXX\" param");
                return false;
            }

            // parse web port
            String sourcePortStr = parseArg(args, "-sourcePort");
            if (sourcePortStr != null) {
                Integer sourcePortI = Integer.valueOf(sourcePortStr);
                if (sourcePortI != null) {
                    sourcePort = sourcePortI;
                } else {
                    System.err.println("Value of \"-sourcePort\" param must be integer.");
                    return false;
                }
            } else {
                System.err.println("Lack \"-sourcePort XXX\" param");
                return false;
            }
        }

        // parse max session Num
        String maxSessionNumString = parseArg(args, "-maxSessionNum");
        if (maxSessionNumString != null) {
            maxSessionNum = Integer.parseInt(maxSessionNumString);
        }

        // parse session period
        String sessionPeriodString = parseArg(args, "-sessionPeriod");
        if (sessionPeriodString != null) {
            sessionPeriod = Integer.parseInt(sessionPeriodString);
        }

        osName = parseArg(args, "-osName");
        if (osName == null) {
            System.err.println("Lack \"-osName XXX\" param");
            return false;
        } else {
            if (!(osName.toLowerCase().equals("linux") || osName.toLowerCase().equals("windows"))) {
                System.err.println("OS:" + osName + "not supported");
                return false;
            }
        }

        return true;
    }

    //endregion

    //region Init

    private boolean init() {
        if ((mode == Consts.START_MODE_REMOTE) && !initSourceData()) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Check server error.");
            return false;
        }

        DbHandler dbHandler = new DbHandler(dbHost, dbName);
        // 连接数据库
        if (!dbHandler.connectToDB()) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Can't connect to database[" + dbHost + ":" + dbName + "].");
            dbHandler.close();
            return false;
        }

        if (!initTrustedConsoleHosts(dbHandler)) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Can't get trusted console hosts.");
            dbHandler.close();
            return false;
        }

        if (!initServers(dbHandler)) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Can't init servers.");
            dbHandler.close();
            return false;
        }

        if (!initWebServer(dbHandler)) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Can't init web server.");
            dbHandler.close();
            return false;
        }

        dbHandler.close();
        return true;
    }

    /**
     * 初始化所有管理的游戏服务器，设置其初始状态为未知状态。
     */
    public void initServers(LinkedList<ServerConfig> serverConfigs) {
        servers = new ConcurrentHashMap<Integer, Server>();

        for (ServerConfig serverConfig : serverConfigs) {
            Server server = new Server(serverConfig, UnknownServerState.INSTANCE);
            servers.put(server.serverConfig.id, server);
        }
    }

    private boolean initSourceData() {
        try {
            // 1. 建立与Manager Server的SSL连接
            String algorithm = Security.getProperty("ssl.TrustManagerFactory.algorithm");
            if (algorithm == null) {
                algorithm = "SunX509";
            }

            SSLContext clientContext = null;

            KeyStore ks = KeyStore.getInstance("JKS");
            ks.load(ManagerToSourceTrustStore.asInputStream(),
                    ManagerToSourceTrustStore.getTrustStorePassword());

            // Set up key manager factory to use our key store
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
            tmf.init(ks);

            // Initialize the SSLContext to work with our key managers.
            clientContext = SSLContext.getInstance("TLS");
            clientContext.init(null, tmf.getTrustManagers(), null);

            SSLSocketFactory sslsocketfactory = clientContext.getSocketFactory();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket(sourceHost, sourcePort);

            // 2. 获取服务器程序
            InputStream inputstream = sslsocket.getInputStream();
            byte[] lenBuf = new byte[4];
            int readLen = 0;
            while (readLen < 4) {
                readLen += inputstream.read(lenBuf, readLen, 4 - readLen);
            }

            int sourceLen = (lenBuf[0] & 0xff) << 24 | (lenBuf[1] & 0xff) << 16 | (lenBuf[2] & 0xff) << 8 | lenBuf[3] & 0xff;
            sourceData = new byte[sourceLen];
            readLen = 0;
            while (readLen < sourceLen) {
                readLen += inputstream.read(sourceData, readLen, sourceLen - readLen);
            }

            return true;
        } catch (Exception exception) {
            exception.printStackTrace();
        }

        return false;
    }

    private boolean initWebServer(DbHandler dbHandler) {
        webServer = new WebServer();
        return webServer.init(dbHandler);
    }

    /**  初始化信任的console服务器IP */
    private boolean initTrustedConsoleHosts(DbHandler dbHandler) {
        trustedConsoleHosts = dbHandler.getTrustedConsoleHosts();

        return !trustedConsoleHosts.isEmpty();
    }

    private boolean initServers(DbHandler dbHandler) {
        LinkedList<ServerConfig> serverConfigs = dbHandler.getServerConfigs();

        if (serverConfigs.isEmpty()) {
            return false;
        }

        initServers(serverConfigs);
        return true;
    }

    //endregion

    /**
     * start方法只能被调用一次
     */
    public void startOnlyOnce() {
        // 启动Web服务
        ServerBootstrap webBootstrap = new ServerBootstrap(
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));
        webBootstrap.setPipelineFactory(new HttpServerToWebPipelineFactory());
        webBootstrap.bind(new InetSocketAddress(webPort));

        // 启动对Console的服务
        consoleToManagerServer = ConsoleToManagerServer.getInstance();
        consoleToManagerServer.init(console2Manager_port, new ConsoleToManagerPipelineFactory());
        consoleToManagerServer.startServer();

        // 启动对Deamon的服务
        deamonToManagerServer = DeamonToManagerServer.getInstance();
        deamonToManagerServer.init(deamon2Manager_port, new DeamonToManagerPipelineFactory());
        deamonToManagerServer.startServer();

        // 启动对Bootstrap的服务
        bootstrapToManagerServer = BootstrapToManagerServer.getInstance();
        bootstrapToManagerServer.init(bootstrap2Manager_port, new BootstrapToManagerPipelineFactory());
        bootstrapToManagerServer.startServer();

        // 启动所有区服务器
        for (Server server : servers.values()) {
            server.tryConnectServer();
        }

        // 启动心跳Timer
        // TODO: 将Timer改成本地Timer
//            startHeartBeatTimer();

        // 启动消息处理线程
        new Thread(this, "managerThread").start();
        System.out.println("managerThread start");
    }

    public boolean isTrustedConsole(String consoleIP) {
        return trustedConsoleHosts.contains(consoleIP);
    }

    public Collection<Channel> getConsoleChannels() {
        return consoleToManagerServer.getConsoleChannels();
    }

    /**
     * 处理消息
     */
    private void handleMessages() {
        // TODO: handle message from message queue
        // 不处理shuttingDown过程中产生的消息
        Message msg = messageQueue.poll();
        while (msg != null) {
            switch (msg.getType()) {
                case ALL_HEARTBEAT: {
                    System.out.printf("manager ALL_HEARTBEAT\n");
                    deamonToManagerServer.handleMessage(msg);

                    for (Server server : servers.values()) {
                        server.handleMessage(msg);
                    }
                    break;
                }
                case MANAGER_DEAMON_HEARTBEAT:
                case MANAGER_DEAMON_CLOSE:
                case MANAGER_DEAMON_CONNECT: {
                    deamonToManagerServer.handleMessage(msg);
                    break;
                }
                case MANAGER_DEAMON_DOWN: {
                    // TODO:
                    break;
                }
                case MANAGER_CONSOLE_CONNECT:
                case MANAGER_CONSOLE_CLOSE: {
                    consoleToManagerServer.handleMessage(msg);
                    break;
                }
                case MANAGER_CLUSTER_CONNECT:
                case MANAGER_CLUSTER_HEARTBEAT:
                case MANAGER_CLUSTER_CLOSE:
                case MANAGER_RUN_SERVER:
                case MANAGER_SHUTDOWN_SERVER:
                case MANAGER_CLEAR_TEST_FLAG:
                case MANAGER_SET_TEST_FLAG:
                case MANAGER_GM:
                case MANAGER_BILL_NOTIFY:
                case MANAGER_FORCE_SET_SERVER_SHUTDOWN_STATUS: {
                    ServerManagerMessage serverManagerMessage = (ServerManagerMessage) msg;
                    try {
                        servers.get(serverManagerMessage.serverId).handleMessage(msg);
                    } catch (Exception ex) {
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, StackTraceUtil.getStackTrace(ex));
                    }
                    break;
                }
                case MANAGER_SYNCCONFIG: {
                    int syncOK = -1;
                    try {
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Config file syncing.");
                        ProcessBuilder processBuilder;
                        if (new File("config").exists()) {
                            processBuilder = new ProcessBuilder("svn", "up", "config");
                        } else {
                            processBuilder = new ProcessBuilder("svn", "checkout", "svn://dev-perforce/mclient/trunk/ProjectKD/配置文件/serverconfig", "config");
                        }
                        boolean redirectErrorStream = processBuilder.redirectErrorStream();
                        if (redirectErrorStream) {
                            Process process = processBuilder.start();
                            new Thread(new ProcessOutputThread(process)).start();
                            process.waitFor();

                            syncOK = 0;
                            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Config file updated.");
                        } else {
                            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Config file redirectErrorStream.");
                        }
                    } catch (Exception ex) {
                        syncOK = -1;
                        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, StackTraceUtil.getStackTrace(ex));
                    }
                    // 将结果发回Console
                    broadcastToConsole(ConsoleToManagerBuilder.buildSyncConfigReturn(syncOK));
                    break;
                }
                case MANAGER_GET_SERVER_STATUS: {
                    int[][] statuses = new int[servers.values().size()][2];
                    int i = 0;
                    for (Iterator<Server> it = servers.values().iterator(); it.hasNext(); ) {
                        Server server = it.next();
                        statuses[i][0] = server.serverConfig.id;
                        statuses[i][1] = server.getServerStatusFlag();
                        i++;
                    }

                    // 将结果发回Console
                    broadcastToConsole(ConsoleToManagerBuilder.buildGetServerStatusReturn(statuses));

                    break;
                }
                case MANAGER_LOAD_SERVER_CONFIG: {
                    // 热加载服务器配置。所要加载的服务器必须是关闭状态或不存在，否则不允许热加载
                    LoadServerConfigMessage loadServerConfigMessage = (LoadServerConfigMessage) msg;

                    if (loadServerConfigMessage.serverConfig != null) {
                        Server server = servers.get(loadServerConfigMessage.serverId);
                        if ((server != null) && (server.state != ShutdownServerState.INSTANCE)) {
                            // 通知Console所要加载配置的服务器不处于关闭状态
                            broadcastToConsole(ConsoleToManagerBuilder.buildLoadServerConfigError(loadServerConfigMessage.serverId, Consts.LOADSERVERCONFIG_ERROR_NOT_SHUTDOWN));
                        } else {
                            if (server != null) {
                                servers.remove(server.getId());
                            }

                            server = new Server(loadServerConfigMessage.serverConfig, UnknownServerState.INSTANCE);
                            servers.put(server.serverConfig.id, server);
                            server.tryConnectServer();

                            // 通知Console加载完成
                            broadcastToConsole(ConsoleToManagerBuilder.buildLoadServerConfigOK(loadServerConfigMessage.serverId));
                        }
                    } else {
                        // 通知Console所要加载配置的服务器不存在
                        broadcastToConsole(ConsoleToManagerBuilder.buildLoadServerConfigError(loadServerConfigMessage.serverId, Consts.LOADSERVERCONFIG_ERROR_NOT_FOUND));
                    }

                    break;
                }
                case MANAGER_BATTLE_WIN_RATE_RESULT: {
                    // 通知Console战斗胜率信息
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Get battle win rate result.");
                    BattleWinRateResultMessage battleWinRateResultMessage = (BattleWinRateResultMessage) msg;
                    broadcastToConsole(ConsoleToManagerBuilder.buildBattleWinRateResult(battleWinRateResultMessage.winRateResult));
                    break;
                }
                case MANAGER_CARD_DRAW_RATE_RESULT: {
                    // 通知Console抽卡掉率信息
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Get card draw rate result.");
                    CardDrawRateResultMessage cardDrawRateResultMessage = (CardDrawRateResultMessage) msg;
                    broadcastToConsole(ConsoleToManagerBuilder.buildCardDrawRateResult(cardDrawRateResultMessage.cardDrawRateResult));
                    break;
                }
                case MANAGER_UNINIT_OCCUPY_INFO: {
                    // 通知Console未初始化臣属信息
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Get uninit occupy info.");
                    UninitOccupyInfoMessage uninitOccupyInfoMessage = (UninitOccupyInfoMessage) msg;
                    broadcastToConsole(ConsoleToManagerBuilder.buildUninitOccupyInfo(uninitOccupyInfoMessage.intValuesProto));
                    break;
                }
                case MANAGER_FIGHTING_OCCUPY_INFO: {
                    // 通知Console战斗中臣属信息
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Get fighting occupy info.");
                    FightingOccupyInfoMessage fightingOccupyInfoMessage = (FightingOccupyInfoMessage) msg;
                    broadcastToConsole(ConsoleToManagerBuilder.buildFightingOccupyInfo(fightingOccupyInfoMessage.intValuesProto));
                    break;
                }
                case MANAGER_SET_DATE: {
                    SetDateMessage setDateMessage = (SetDateMessage) msg;
                    setDate(setDateMessage.date);
                    break;
                }
                case MANAGER_SET_TIME: {
                    SetTimeMessage setTimeMessage = (SetTimeMessage) msg;
                    setTime(setTimeMessage.time);
                    break;
                }
                case MANAGER_GET_DATE_TIME: {
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Get current datetime.");
                    broadcastToConsole(ConsoleToManagerBuilder.buildDateTime(System.currentTimeMillis()));
                    break;
                }
                default:
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "default unknow case.");
                    break;
            }
            msg = messageQueue.poll();
        }
    }

    private void setDate(long date) {
        if (osName.toLowerCase().equals("windows")) {
            Date dt = new Date(date);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            String sDate = sdf.format(dt);
            String cmd = "cmd /c date " + sDate;
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Set system date:" + sDate);
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Command: " + cmd);
            try {
                Process p = Runtime.getRuntime().exec(cmd);
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Exit code: " + p.waitFor());
            } catch (InterruptedException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            } catch (IOException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        } else {
            // linux
            long currentTime = System.currentTimeMillis();
            long millisecondInDay = (currentTime + Consts.JET_LAG) % Consts.MILSECOND_ONE_DAY;

            Date dt = new Date(date + millisecondInDay);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String sDateTime = sdf.format(dt);
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Set system dateTime: " + sDateTime);
            try {
                Process p = Runtime.getRuntime().exec(new String[]{"date", "-s", sDateTime});
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Exit code: " + p.waitFor());
            } catch (InterruptedException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            } catch (IOException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        }
    }

    private void setTime(long time) {
        if (osName.toLowerCase().equals("windows")) {
            Date dt = new Date(time);

            SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
            String sTime = sdf.format(dt);
            String cmd = "cmd /c time " + sTime;
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Set system time:" + sTime);
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Command: " + cmd);
            try {
                Process p = Runtime.getRuntime().exec(cmd);
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Exit code: " + p.waitFor());
            } catch (InterruptedException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            } catch (IOException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        } else {
            // linux
            long currentTime = System.currentTimeMillis();
            long millisecondOfDay = (currentTime + Consts.JET_LAG) / Consts.MILSECOND_ONE_DAY * Consts.MILSECOND_ONE_DAY;

            Date dt = new Date(millisecondOfDay + time);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String sDateTime = sdf.format(dt);
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Set system dateTime: " + sDateTime);
            try {
                Process p = Runtime.getRuntime().exec(new String[]{"date", "-s", sDateTime});
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_INFO, "Exit code: " + p.waitFor());
            } catch (InterruptedException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            } catch (IOException ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        }
    }

    public void broadcastToConsole(ProtobufMessage protobufMessage) {
        Collection<Channel> consoleChannels = getConsoleChannels();
        for (Channel channel : consoleChannels) {
            channel.write(protobufMessage);
        }
    }

    public Server getServer(int serverId) {
        return servers.get(serverId);
    }

    public ConcurrentHashMap<Integer, Server> getServers() {
        return servers;
    }

    public ChannelContext getDeamonChannelContext(String deamonHost) {
        return deamonToManagerServer.getChannelContext(deamonHost);
    }

    private void flushNetData() {
        deamonToManagerServer.flush();
        for (Server server : servers.values()) {
            server.flush();
        }
    }

    @Override
    public void run() {
        while (true) {
            try {
                // 处理消息
                handleMessages();
                flushNetData();

                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
                }
            } catch (Exception ex) {
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, StackTraceUtil.getStackTrace(ex));
            }
        }
    }

    private void startHeartBeatTimer() {
        heartbeatTimer = new Timer();
        heartbeatTimer.schedule(new TimerTask() {

            @Override
            public void run() {
                ServerMessageQueue.queue().offer(MessageBuilder.buildHeartbeatMessage());
            }
        }, HEARTBEAT_PERIOD, HEARTBEAT_PERIOD);
    }

    public void setTrustedConsoleHosts(String[] trustedConsoleHosts) {
        this.trustedConsoleHosts.addAll(Arrays.asList(trustedConsoleHosts));
    }

}
